import { VNodeDirective } from '@/interface/types/vnode';
import { h, render } from 'vue';
import Icon from '@/components/Icon.vue';
import { isObject } from '@/utils';

interface ILoadingElemen extends HTMLElement {
  _loading?: HTMLElement;
  _oldPosition?: String;
}

interface ILoadingConfig {
  color?: string;
  width?: string;
  height?: string;
  icon?: string;
  bg?: string;
}

const createLoadingWrapper: (w: number, h: number) => HTMLElement = (width, height) => {
  const loading = document.createElement('div');
  loading.classList.add('v-loading-wrapper');
  loading.classList.add('v-shadow');
  loading.classList.add('v-radius');
  loading.style.width = `${width}px`;
  loading.style.height = `${height}px`;
  loading.style.backgroundColor = ``;
  return loading;
}

const craeteIcon = (container: HTMLElement, config: ILoadingConfig) => {
  const { color='#409EFF', icon='icon-icon_A', width='2me', height='2em' } = config;
  const options = { color, icon, width, height };
  const instance = h(Icon, options);
  render(instance, container);
}

const loadingShow = (el: ILoadingElemen, config: ILoadingConfig = {}) => {
  if (el._loading) return;
  // 1.获取父元素的宽高定位
  const rect = el.getBoundingClientRect();
  const { width, height } = rect;
  // 2.设置父元素的position为relative
  el._oldPosition = el.style.position;
  el.style.position = 'relative';
  // 3.创建加载元素
  const loading = createLoadingWrapper(width, height);
  // 4.创建icon
  craeteIcon(loading, config);
  let bg = 'rgba(255, 255, 255, .7)';
  if (config.bg) {
    bg = config.bg;
  }
  loading.style.backgroundColor = bg;
  el._loading = loading;
  el.appendChild(loading);
}

const loadingHiden = (el: ILoadingElemen) => {
  if (!el._loading) return;
  el.style.position = el._oldPosition as string;
  el.removeChild(el._loading);
  delete el._loading;
  delete el._oldPosition;
}

const unifyCall = (el: ILoadingElemen, binding: VNodeDirective) => {
  const value = typeof binding.value === 'boolean' 
    ? binding.value : isObject(binding.value) 
    ? binding.value.show : true;
  value ? (isObject(binding.value) ? loadingShow(el, binding.value) : loadingShow(el)) 
    : loadingHiden(el);
}

const mounted = (el: ILoadingElemen, binding: VNodeDirective) => {
  unifyCall(el, binding);
}

const updated = (el: ILoadingElemen, binding: VNodeDirective) => {
  unifyCall(el, binding);
}

const beforeUnmount = (el: ILoadingElemen) => loadingHiden(el);

export const Loading = {
  mounted,
  updated,
  beforeUnmount
}